package belf.migrate.engine.job.structure.helper;

import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.*;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.DbObjectType;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.util.PostLog;
import belf.migrate.api.util.TableNameComparator;
import belf.migrate.engine.job.JobResult;
import belf.migrate.api.job.Job;
import belf.migrate.engine.job.JobResultItem;
import belf.migrate.api.util.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;
import java.util.List;

import static belf.migrate.api.job.JobCode.*;

@Slf4j
public class CreateForeignKeyTool extends TableTool {

    public CreateForeignKeyTool(JobContext jobContext, Job job) {
        super(jobContext, job);
    }

    public JobResult run() {
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建外键开始！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        JobResult jobResult = new JobResult(SUCCESS, "外键成功删除");
        AbstractJdbcCatalog sinkCatalog = jobContext.getSinkCatalog();
        for (CatalogTable catalogTable: jobContext.getSourcetableMap().values()) {
            String startTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            TablePath sourceTablePath = catalogTable.getTablePath();

            // 若限定了要处理的表名称
            if (job.getMissions() != null &&
                    !job.getMissions().isEmpty() &&
                    TableNameComparator.containsTable(job.getMissions(), sourceTablePath.getTableName()) ) {
                jobContext.addProgress(DbObjectType.FOREIGN_KEY.getTypeName(), job.getMissions().size());
                JobResultItem jobResultItem = process(sinkCatalog, sourceTablePath, catalogTable, startTime);
                jobResult.addItem(jobResultItem);
            } else { // 若没有限定表，则是处理所有表
                jobContext.addProgress(DbObjectType.FOREIGN_KEY.getTypeName(), jobContext.getSourcetableMap().size());
                JobResultItem jobResultItem = process(sinkCatalog, sourceTablePath, catalogTable, startTime);
                jobResult.addItem(jobResultItem);
            }

        }

        if (jobResult.getFailtures() > 0) {
            jobResult.setCode(CREATE_TABLE_ERROR);
            jobResult.setMessage("创建外键失败");
            log.error("Create Some ForeignKeys Of {} Failed.", jobContext.getValidSinkDatabase());
        } else {
            log.info("Create All ForeignKeys Of {} Successfully.", jobContext.getValidSinkDatabase());
        }
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建外键结束！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        return jobResult;
    }

    private JobResultItem process(Catalog catolog, TablePath sourceTablePath, CatalogTable catalogTable, String startTime) {
        String sinkCatalogName = jobContext.getTaskConf().getSinkConf().getCatalogName();
        String sinkDatabaseName = jobContext.getTaskConf().getSinkConf().getDatabaseName();
        String sinkSchemaName = jobContext.getTaskConf().getSinkConf().getSchemaName();
        QualifiersModel qualifiersModel = getSinkQualifiersModel();
        TablePath sinkTablePath = TablePath.of(sinkDatabaseName,
                sinkSchemaName,
                sourceTablePath.getTableName(),
                qualifiersModel);

        JobResultItem jobResultItem = new JobResultItem(
                sourceTablePath.getTableName(),
                sinkTablePath.getCaseTableName(),
                SUCCESS,
                "创建表" + sourceTablePath.getTableName() + "的外键成功");
        jobResultItem.setStartTime(startTime);
        jobResultItem.setType(DbObjectType.FOREIGN_KEY.getTypeName());

        TableIdentifier tableIdentifier = TableIdentifier.of(sinkCatalogName,
                sinkDatabaseName,
                sinkSchemaName,
                sourceTablePath.getTableName(),
                sourceTablePath.getQualifiersModel());
        CatalogTable sinCatalogTable = CatalogTable.of(tableIdentifier,
                catalogTable.getTableSchema(),
                catalogTable.getOptions(),
                catalogTable.getComment(),
                sinkCatalogName);
        try {
            catolog.createForeignKey(sinkTablePath, sinCatalogTable, false, jobContext.getTaskConf());
            List<String> foreignSqls = catolog.getCreateForeignSqls(sinkTablePath, sinCatalogTable, jobContext.getTaskConf());
            if (!CollectionUtils.isEmpty(foreignSqls)) {
                jobResultItem.setSinkDdl(String.join(" \n ", foreignSqls));
            }
            log.info("Table {} foreign key created!", tableIdentifier.toString());
        } catch (Exception e) {
            e.printStackTrace();
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "创建表" + sourceTablePath.getTableName() + "的外键失败",
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            return new JobResultItem(
                    sourceTablePath.getTableName(),
                    sinkTablePath.getCaseTableName(),
                    CREATE_FOREIGNKEY_ERROR,
                    "创建表" + sourceTablePath.getTableName() + "的外键失败",
                    ExceptionUtils.toString(e),
                    DbObjectType.FOREIGN_KEY.getTypeName(),
                    startTime,
                    endTime,
                    null,
                    null);
        }

        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建表" + sourceTablePath.getTableName() + "的外键成功",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        jobResultItem.setEndTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        return jobResultItem;
    }
}
